﻿using System;
using System.Reflection;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel;
using Inovout.Repositories;
using System.IO;
using Inovout.Models;

namespace Inovout.Services
{
    public class ServiceComponentRegistrar : ComponentRegistrar
    {
        public override void Register()
        {
            Container.Kernel.ConfigurationStore.AddFacilityConfiguration(typeof(NHibernateExtensionFacility).FullName
                , NHibernateExtensionFacility.GetNHibernateFacilityConfiguration(Container.Resolve<IApplication>()));
            Container.AddFacility<NHibernateExtensionFacility>();

        }
        public override void Register(Assembly assembly)
        {
            Container.Register(
              AllTypes.FromAssembly(assembly)
                    .BasedOn(typeof(DataObjectRepository<>))
                    .WithService.AllInterfaces()
                    .Configure(delegate(ComponentRegistration component)
                    {
                        component.LifeStyle.Transient
                            .OnCreate(BuildRepository);
                    }),
                AllTypes.FromAssembly(assembly)
                    .BasedOn(typeof(RepositoryService<>))
                    .WithService.AllInterfaces()
                    .Configure(delegate(ComponentRegistration component)
                    {
                        component.LifeStyle.Transient
                           .Named(component.Implementation.GetType().Name.Substring(1).ToLower())
                            .OnCreate(BuildService);
                    }),
                 AllTypes.FromAssembly(assembly)
                    .BasedOn(typeof(DataObject))
                    .WithService.Base()
                    .WithService.AllInterfaces()
                    .Configure(delegate(ComponentRegistration component)
                    {
                        component.LifeStyle.Transient.Named(component.Implementation.ToString());

                        Type dataObjectType = component.Implementation;
                        RepositoryConfigurationKeyDictionary repositoryConfigurationKeyDictionary = Container.Resolve<RepositoryConfigurationKeyDictionary>();
                        NHibernate.Cfg.Configuration configuration = Container.Resolve<NHibernate.Cfg.Configuration>(repositoryConfigurationKeyDictionary.GetDatabaseName(dataObjectType) + ".cfg");
                        Stream cfgStream = null;
                        if (assembly.GetManifestResourceInfo(dataObjectType.FullName + ".hbm.xml") == null)
                        {
                            cfgStream = assembly.GetManifestResourceStream(dataObjectType.FullName);
                        }
                        else
                        {
                            cfgStream =
                            assembly.GetManifestResourceStream(dataObjectType.FullName + ".hbm.xml");
                        }
                        if (cfgStream != null)
                        {
                            using (cfgStream)
                            {
                                configuration.AddInputStream(cfgStream);
                            }
                        }
                        //if (IsDebug)
                        //{
                        //    SchemaUpdate schemaUpdate = new SchemaUpdate(configuration);
                        //    schemaUpdate.Execute(false, true);
                        //}

                        Type repositoryInterfaceType = typeof(IRepository<>).MakeGenericType(dataObjectType);
                        Type repositoryImplementationType = typeof(DataObjectRepository<>).MakeGenericType(dataObjectType);
                        Type repositoryServiceInterfaceType = typeof(IRepositoryService<>).MakeGenericType(dataObjectType);
                        Type repositoryServiceImplementationType = typeof(RepositoryService<>).MakeGenericType(dataObjectType);

                        Container.Register(
                         Component.For(repositoryInterfaceType)
                             .OnlyNewServices()
                            .ImplementedBy(repositoryImplementationType)
                            .LifeStyle.Transient
                            .OnCreate(BuildRepository),
                               Component.For(repositoryServiceInterfaceType)
                             .OnlyNewServices()
                            .ImplementedBy(repositoryServiceImplementationType)
                            .LifeStyle.Transient
                            .OnCreate(BuildService)
                            );
                    })

                //Component.For(typeof(IRepositoryService<>))
                //     .Unless((kernel, componentModel) => kernel.HasComponent(componentModel.Service))
                //     .ImplementedBy(typeof(RepositoryService<>))
                //    .LifeStyle.Transient
                //    .OnCreate((kernel, instance) => {

                //        BuildService(kernel, instance);
                //    })
                );
        }

        private void BuildService(IKernel kernel, object instance)
        {
            BaseService service = instance as BaseService;
            if (service.logger == null)
            {
                service.logger = Logger.Create(service.GetType());
            }
            Type modelType = GetModelType(service.GetType());
            if (service.repository == null && modelType != null)
            {
                Type repositoryType = typeof(IRepository<>);
                repositoryType = repositoryType.MakeGenericType(modelType);
                service.repository = kernel.Resolve(repositoryType) as BaseRepository;
            }
        }
        private void BuildRepository(IKernel kernel, object instance)
        {
            BaseRepository repository = instance as BaseRepository;
            if (repository.logger == null)
            {
                repository.logger = Logger.Create(repository.GetType());
            }
            if (repository.sessionManager == null)
            {
                repository.sessionManager = kernel.Resolve<ITypeSessionManager>();
            }
        }

        private static Type GetModelType(Type serviceType)
        {
            Type repositoryServiceInterfaceType = serviceType.GetInterface(typeof(IRepositoryService<>).Name);

            if (repositoryServiceInterfaceType == null)
            {
                return null;
            }
            else
            {
                return repositoryServiceInterfaceType.GetGenericArguments()[0];
            }
        }
    }
}
